package com.george.payment.wechat.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.george.common.entity.dto.Result;
import com.george.common.entity.vo.payment.MallWeixinPayReq;
import com.george.payment.wechat.config.WeixinPayProperties;
import com.george.payment.wechat.config.WxPayConfig;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.UUID;

/**
 * @title: WeixinPayService.java
 * @description: 微信支付服务
 * @author: George
 * @date: 2023/6/25 16:51
 */
@Service
@Slf4j
public class WeixinPayService {

    @Resource
    private WxPayConfig wxPayConfig;

    @Resource
    private WeixinPayProperties weixinPayProperties;

    /**
     * 微信JSAPI下单：商户系统先调用该接口在微信支付服务后台生成预支付交易单，返回正确的预支付交易会话标识后再按Native、JSAPI、APP等不同场景生成交易串调起支付。
     * 微信接口文档：https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_1.shtml
     *
     * @param payParam
     * @return com.george.common.entity.dto.Result
     * @author George
     * @date 2023/6/28 9:30
     */
    public Result jsapiPay(MallWeixinPayReq payParam) {
        try {
            HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
            httpPost.addHeader("Accept", "application/json");
            httpPost.addHeader("Content-type", "application/json; charset=utf-8");
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectMapper objectMapper = new ObjectMapper();
            ObjectNode rootNode = objectMapper.createObjectNode();
            rootNode.put("mchid", weixinPayProperties.getMerchantId())
                    .put("appid", weixinPayProperties.getAppId())
                    .put("out_trade_no", payParam.getOrderNo())
                    .put("openid", payParam.getOpenid())
                    .put("description", "Image形象店-深圳腾大-QQ公仔")
                    .put("notify_url", weixinPayProperties.getNotifyUrl());
            rootNode.putObject("amount")
                    .put("total", 1);
            rootNode.putObject("payer");
            objectMapper.writeValue(bos, rootNode);
            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
            CloseableHttpResponse response = wxPayConfig.getWxPayHttpClient().execute(httpPost);
            String bodyAsString = EntityUtils.toString(response.getEntity());
            log.info("用户发起微信 jsapiPay 支付,微信响应：{}", bodyAsString);
            JSONObject resultJson = JSON.parseObject(bodyAsString);
            if (Objects.nonNull(resultJson)) {
                JSONObject jsonObject = new JSONObject();
                // 时间戳，标准北京时间，时区为东八区，自1970年1月1日 0点0分0秒以来的秒数。注意：部分系统取到的值为毫秒级，需要转换成秒(10位数字)。
                long timestamp = System.currentTimeMillis() / 1000;
                // 随机字符串	nonceStr 不长于32位
                String nonceStr = UUID.randomUUID().toString().replace("-","");
                // 预付单号
                String prepayId = resultJson.getString("prepay_id");
                // 订单详情扩展字符串 package 小程序下单接口返回的prepay_id参数值，提交格式如：prepay_id=***
                String packageStr = "prepay_id=" + prepayId;
                //签名方式 signType 签名类型，默认是RSA，仅支持RSA
                // 签名，使用字段appId、timeStamp、nonceStr、package计算得出的签名值
                StringBuilder sb = new StringBuilder();
                // 应用id
                sb.append(weixinPayProperties.getAppId()).append("\n");
                // 支付签名时间戳
                sb.append(timestamp).append("\n");
                // 随机字符串
                sb.append(nonceStr).append("\n");
                // 预支付单号
                sb.append(packageStr).append("\n");
                // 签名
                String paySign = wxPayConfig.paySign(sb.toString());
                jsonObject.put("timestamp", timestamp);
                jsonObject.put("nonceStr", nonceStr);
                jsonObject.put("package", packageStr);
                jsonObject.put("signType", "RSA");
                jsonObject.put("paySign",paySign);
                return Result.successResult(jsonObject);
            } else {
                return Result.failResult("微信下单响应为空！");
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            log.info("用户发起微信 jsapiPay 支付，发生异常，异常信息：{}", ex.getMessage());
            return Result.failResult("系统异常");
        }
    }

    /**
     * 微信扫码支付：调用微信统一下单接口，生成付款二维码
     * 微信接口文档：https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_1.shtml
     *
     * @param payParam
     * @return com.george.common.entity.dto.Result
     * @author George
     * @date 2023/6/28 9:31
     */
    public Result nativePay(MallWeixinPayReq payParam) {
        try {
            HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/native");
            httpPost.addHeader("Accept", "application/json");
            httpPost.addHeader("Content-type", "application/json; charset=utf-8");
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectMapper objectMapper = new ObjectMapper();
            ObjectNode rootNode = objectMapper.createObjectNode();
            rootNode.put("mchid", weixinPayProperties.getMerchantId())
                    .put("appid", weixinPayProperties.getAppId())
                    .put("notify_url", weixinPayProperties.getNotifyUrl())
                    .put("description", "Image形象店-深圳腾大-QQ公仔")
                    .put("out_trade_no", payParam.getOrderNo());
            rootNode.putObject("amount")
                    .put("total", 1);
            objectMapper.writeValue(bos, rootNode);
            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
            CloseableHttpResponse response = wxPayConfig.getWxPayHttpClient().execute(httpPost);
            String bodyAsString = EntityUtils.toString(response.getEntity());
            log.info("用户发起微信 Native 支付,微信响应：{}", bodyAsString);
            JSONObject resultJson = JSON.parseObject(bodyAsString);
            if (Objects.nonNull(resultJson)) {
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("codeUrl", resultJson.getString("code_url"));
                return Result.successResult(jsonObject);
            } else {
                return Result.failResult("微信下单响应为空！");
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            log.info("用户发起微信 Native 支付，发生异常，异常信息：{}", ex.getMessage());
            return Result.failResult("系统异常");
        }
    }

    /**
     * 微信支付订单结果查询
     * 微信接口文档：https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_2.shtml
     *
     * @param payReq
     * @return com.george.common.entity.dto.Result
     * @author George
     * @date 2023/6/26 16:03
     */
    public Result wechatPayOrderQuery(MallWeixinPayReq payReq) {
        try {
            String orderNo = payReq.getOrderNo();
            if (StringUtils.isEmpty(orderNo)) {
                return Result.failResult("订单号不能为空");
            }
            HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/" + payReq.getOrderNo());
            CloseableHttpResponse response = wxPayConfig.getWxPayHttpClient().execute(httpGet);
            String bodyAsString = EntityUtils.toString(response.getEntity());
            log.info("用户发起微信下单结果查询,微信响应：{}", bodyAsString);
            JSONObject resultJson = JSON.parseObject(bodyAsString);
            if (Objects.nonNull(resultJson)) {
                JSONObject jsonObject = new JSONObject();
                /**
                 * 交易状态，枚举值：
                 * SUCCESS：支付成功
                 * REFUND：转入退款
                 * NOTPAY：未支付
                 * CLOSED：已关闭
                 * REVOKED：已撤销（仅付款码支付会返回）
                 * USERPAYING：用户支付中（仅付款码支付会返回）
                 * PAYERROR：支付失败（仅付款码支付会返回）
                 */
                jsonObject.put("tradeState", resultJson.getString("trade_state"));
                /**
                 * 交易状态描述
                 */
                jsonObject.put("tradeStateDesc", resultJson.getString("trade_state_desc"));
                /**
                 * 支付完成时间
                 */
                jsonObject.put("successTime", resultJson.getDate("success_time"));
                return Result.successResult(jsonObject);
            } else {
                return Result.failResult("微信下单结果为空！");
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            log.info("用户发起微信下单结果查询，发生异常，异常信息：{}", ex.getMessage());
            return Result.failResult("系统异常");
        }
    }

    /**
     * 用户微信支付成功后，回调通知接口(提供给微信调用)
     * 微信接口文档：https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_5.shtml
     *
     * @param request
     * @return com.george.common.entity.dto.Result
     * @author George
     * @date 2023/6/28 9:50
     */
    public Result notify(HttpServletRequest request) {
        try {
            //请求体
            String body = IOUtils.toString(request.getInputStream());
            JSONObject resultObject = JSON.parseObject(body);
            JSONObject resource = resultObject.getJSONObject("resource");
            String ciphertext = resource.getString("ciphertext");
            String nonce = resource.getString("nonce");
            String associatedData = resource.getString("associated_data");
            AesUtil aesUtil = new AesUtil(weixinPayProperties.getApiV3key().getBytes(StandardCharsets.UTF_8));
            //密文解密
            /**
             * {
             *     "transaction_id":"1217752501201407033233368018",
             *     "amount":{
             *         "payer_total":100,
             *         "total":100,
             *         "currency":"CNY",
             *         "payer_currency":"CNY"
             *     },
             *     "mchid":"1230000109",
             *     "trade_state":"SUCCESS",
             *     "bank_type":"CMC",
             *     "promotion_detail":[],
             *     "success_time":"2018-06-08T10:34:56+08:00",
             *     "payer":{
             *         "openid":"oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"
             *     },
             *     "out_trade_no":"1217752501201407033233368018",
             *     "appid":"wxd678efh567hg6787",
             *     "trade_state_desc":"支付成功",
             *     "trade_type":"MICROPAY",
             *     "attach":"自定义数据",
             *     "scene_info":{
             *         "device_id":"013467007045764"
             *     }
             * }
             */
            String resultStr = aesUtil.decryptToString(
                    associatedData.getBytes(StandardCharsets.UTF_8),
                    nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
            JSONObject wechatPayNotifyResultOb = JSON.parseObject(resultStr);
            //交易状态
            String tradeState = wechatPayNotifyResultOb.getString("trade_state");
            //总订单号
            String orderNo = wechatPayNotifyResultOb.getString("out_trade_no");
            if ("SUCCESS".equals(tradeState)) {
                // TODO: 2023/6/28 完成支付，修改订单状态等
            } else {
                // TODO: 2023/6/26 未支付成功
            }
            return Result.successResult("处理成功");
        } catch (Exception ex) {
            ex.printStackTrace();
            log.info("接收微信端支付回调通知处理，发生异常，异常信息：{}", ex.getMessage());
            return Result.failResult("系统异常");
        }

    }


}
